""" This module is used for translating a pi-graph into a Petri-net.
"""
from pi.action import Input, Output, ComRules, StrlType
from pi.context import ContextBuilder, Generator
from pi.graph import PiGraphBuilder
from snakes.nets import Transition, Variable, Place, Expression, PetriNet, MultiArc
   
class NameGenerator:
    """ Generating names for places and transitions.
    """
    def __init__(self):
        pass
    
    def placeName(self, rname=None, v=None):
        """ Generate a place name
        @param rname: replicator name, omitted if generate a context place name
        @param v: action vertex, omitted if generate an initial place
        """
        if rname is None:  # context place
            return "pG"
        elif v is None:  # initial place
            return "p0_%s" % rname
        else:  # normal place
            return "p%d_%s" % (v, rname)
        
    def transName(self, x, y):
        """ Generate transition name
        @note: if y is a number then generate an atomic transition name
            if both x and y a are two strings, then sync transition.
        """
        if isinstance(y, int):
            return "t%d%s" % (y, x)  # atomic
        else:
            return "S%s_%s" % (x, y)  # sync
        
    def tokenX(self, a=None):
        """ Generate arc label X
        @param a: the action corresponding to the transition 
        """
        if a is None:
            return "X"
        if isinstance(a, Input):  # label for input
            return "Xi"
        elif isinstance(a, Output):
            return "Xo"
        else:
            return "Xs"  # label for silence
        
    def tokenI(self, a):
        """ Generate arc label I
        @param a: the action corresponding to the transition
        """
        if isinstance(a, Input):
            return "Ii"
        elif isinstance(a, Output):
            return "Io"
        else:  # for silence
            return "Is"
    
    def isInitialPlace(self, s):
        """ Test if s is a name of initial place 
        """
        return s.startswith("p0_")
    
    
class NetTransition(object):
    """ A base class for transitions
    """
    def __init__(self):
        raise NotImplementedError("abstract class")
    
    def cons(self, gen, n=None):
        """ Remove minimal threads
        @param gen: the thread generator (set of available thread)
        @param n: which thread will be removed: 
            1-the first minimum,
            2-second minimum, 
            None-both the first and the second minimums
        @return: the rest of threads
        """
        if not isinstance(gen, Generator):
            raise Exception("need a thread generator")
        if n is None:  # consume two minimum threads
            i, g = gen.take()
            i, g = g.take()
            return g
        elif n == 1:  # consume the first minimum
            i, g = gen.take()
            return g
        elif n == 2:  # consume the second minimum
            i, g = gen.take()
            j, g = g.take()
            return g.put(i)
        else: raise Exception()
    
    def prod(self, gen, n=1):
        """ Producing threads
        @param gen: the thread generator
        @param n: which thread is produced
            1 - the first minimum (default)
            2 - the second minimum
        @return: the thread that is produced
        """
        if not isinstance(gen, Generator):
            raise Exception()
        if n == 1:
            i, g = gen.take()
            return i
        elif n == 2:
            i, g = gen.take()
            i, g = g.take()
            return i
        else: raise Exception()
        
    def adds(self, gen, i, j=None):
        """ Put threads back to the generator
        @param gen: the generator
        @param i, j: the thread are added
        @return: the generator after adding threads
        """
        if not isinstance(gen, Generator):
            raise Exception()
        g = gen.put(i)
        if j is not None:
            g = g.put(j)
        return g
    
    def extractThread(self, num, token1, token2=None):
        """ Get threads from tokens
        @param num: number of thread is extracted, n=1,2
        @param token1, token2: token that contains threads
        @return: two extracted threads, or None for each if
            the tokens has no threads.
        @note: if n=2 and token2 is omitted then both two threads
            are extracted from token1, which is a generator
        """
        if num == 1:
            i = None
            if isinstance(token1, Generator):
                if not token1.empty():
                    i = token1.min()
            else:
                i = token1
            return i
        elif num == 2:
            i1 = i2 = None
            if token2 is None:
                if not isinstance(token1, Generator):
                    raise TypeError()
                if not token1.empty():
                    i1, token1 = token1.take()
                if not token1.empty():
                    i2, token1 = token1.take()
            elif (isinstance(token1, Generator) 
                    and isinstance(token2, Generator)):
                if not token1.empty():
                    i1 = token1.min()
                if not token2.empty():
                    i2 = token2.min()
            elif isinstance(token1, Generator):
                i2 = token2
                if not token1.empty():
                    i1 = token1.min()
            elif isinstance(token2, Generator):
                i1 = token1
                if not token2.empty():
                    i2 = token2.min()
            else:
                i1 = token1
                i2 = token2
            return (i1, i2)
        else: raise ValueError()

           
class NetAtomicTransition(NetTransition, StrlType):
    """ Transition of atomic actions: input, output, silent one
    """
    def __init__(self, ga, rname, graph, stype):
        """
        @param ga: guarded action
        @param rname: replicator name
        @param graph: the pi-graph
        @param stype: structural type of the action
        """
        self._ga = ga
        self._r = rname
        self._grp = graph
        StrlType.__init__(self, stype)
    
    def guard(self):
        return self._ga.guard()
    
    def action(self):
        return self._ga.action()
    
    def getGuardedAction(self):
        return self._ga
    
    def rname(self):
        return self._r
    
    def getGraph(self):
        return self._grp
    
    def pre(self, nc, tk):
        """ Check if the transition can fire
        @param nc: name context
        @param tk: input token
        """
        i = self.extractThread(1, tk)
        if i is None:
            return False
        if self.isMono() or self.isSpwn():
            nc = nc.init(self._r, i, self._grp)
        g = self._ga.guard()
        a = self._ga.action()
        return a.pre(nc, g, i)
    
    def post(self, nc, tk):
        """ Compute a new name context
        @param nc: name context
        @param tk: input token
        """
        i = self.extractThread(1, tk)
        a = self._ga.action()
        g = self._ga.guard()
        
        if self.isMono():
            nc = nc.init(self._r, i, self._grp)
            nc = a.post(nc, g, i)
            nc = nc.reset(self._r, i)
        elif self.isSpwn():
            nc = nc.init(self._r, i, self._grp)
            nc = a.post(nc, g, i)
        elif self.isTerm():
            nc = a.post(nc, g, i)
            nc = nc.reset(self._r, i)
        else:
            nc = a.post(nc, g, i)
        return nc.normalize()
    
    def __str__(self):
        return "%s" % self._ga
    
    def __repr__(self):
        return "%r" % self._ga
        

class NetSyncTransition(NetTransition, ComRules):
    """ A transition of a sync action
    """
    def __init__(self, atomicOut, atomicIn):
        """
        @param atomicOut: atomic output action
        @param atomicIn: atomic input action
        """
        self._ai = atomicIn
        self._ao = atomicOut
        
    def __str__(self):
        return "[%s|%s]" % (self._ao, self._ai)
    
    def __repr__(self):
        return "%[r | %r]" % (self._ao, self._ai)
        
    def pre(self, nc, tkO, tkI=None):
        """ Precondition for a synchronization
        @param nc: name context
        @param tkO: token used for the output, 
            or both output and input if tkI is omitted
        @param tkI: token used for the input
        """
        gr = self._ai.getGraph()
        io, ii = self.extractThread(2, tkO, tkI)
        if io is None or ii is None:
            return False
        
        ro = self._ao.rname()
        go = self._ao.guard()  # guard-out
        co = self._ao.action().channel()
        ri = self._ai.rname()
        gi = self._ai.guard()
        ci = self._ai.action().channel()
        
        if self._ao.isMono() or self._ao.isSpwn():
            nc = nc.init(ro, io, gr)
        if self._ai.isMono() or self._ai.isSpwn():
            nc = nc.init(ri, ii, gr)
            
        if not nc.check(nc.substitute(go, io)):
            return False
        NC1 = nc.eval(nc.substitute(go, io))
        if not NC1.check(NC1.substitute(gi, ii)):
            return False
        NC2 = NC1.eval(NC1.substitute(gi, ii))
        cco = NC2.instance(co, io)  # instance of out-channel
        cci = NC2.instance(ci, ii)  # instance of in-channel
        partO = NC2.partitions().val(cco)
        partI = NC2.partitions().val(cci)
        return not NC2.distinctions().distinct(partO, partI)
    
    def post(self, nc, tkO, tkI=None):
        """ Post condition for a synchronization
        """
        ro = self._ao.rname()
        ri = self._ai.rname()
        gao = self._ao.getGuardedAction()
        gai = self._ai.getGuardedAction()
        gr = self._ai.getGraph()
        
        io, ii = self.extractThread(2, tkO, tkI)        
        if self._ao.isMono() and self._ai.isMono():
            nc = self.syncMono_Mono(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isMono() and self._ai.isSpwn():
            nc = self.syncMono_Spaw(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isMono() and self._ai.isTerm():
            nc = self.syncMono_Term(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isMono() and self._ai.isNorm():
            nc = self.syncMono_Norm(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isSpwn() and self._ai.isMono():
            nc = self.syncSpaw_Mono(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isSpwn() and self._ai.isSpwn():
            nc = self.syncSpaw_Spaw(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isSpwn() and self._ai.isTerm():
            nc = self.syncSpaw_Term(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isSpwn() and self._ai.isNorm():
            nc = self.syncSpaw_Norm(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isTerm() and self._ai.isMono():
            nc = self.syncTerm_Mono(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isTerm() and self._ai.isSpwn():
            nc = self.syncTerm_Spaw(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isTerm() and self._ai.isTerm():
            nc = self.syncTerm_Term(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isTerm() and self._ai.isNorm():
            nc = self.syncTerm_Norm(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isNorm() and self._ai.isMono():
            nc = self.syncNorm_Mono(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isNorm() and self._ai.isSpwn():
            nc = self.syncNorm_Spaw(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isNorm() and self._ai.isTerm():
            nc = self.syncNorm_Term(nc, ro, io, gao, ri, ii, gai, gr)
        elif self._ao.isNorm() and self._ai.isNorm():
            nc = self.com(nc, io, gao, ii, gai)
        return nc.normalize()
    

class TranslationRules(NameGenerator):
    """ Rule for translation
    """
    def __init__(self):
        pass
        
    def tranMono(self, net, v, r, gr):
        """ Translate a mono, atomic action
        @param net: the net
        @param r,v: position of the action
        @param gr: the pi-graph
        """
        tname = self.pushAtomic(net, r, v, gr, StrlType.MONO)
        p0 = self.placeName(r.name())
        tkX = self.tokenX(r.node(v).action())
        net.add_input(p0, tname, Variable("%s" % tkX))
        net.add_output(p0, tname, Variable("%s" % tkX))
        self.makeGuardAtomic(net, tname, tkX)
                    
    def tranSpwn(self, net, v, r, gr):
        """ Translate a spawning, atomic action
        @see: transMono
        """
        tname = self.pushAtomic(net, r, v, gr, StrlType.SPWN)
        pv = self.placeName(r.name(), v)
        net.add_place(Place(pv))
        p0 = self.placeName(r.name())
        tkX = self.tokenX(r.node(v).action())
        net.add_input(p0, tname, Variable("%s" % tkX))
        net.add_output(p0, tname, Expression("%s.cons(%s, 1)" % (tname, tkX)))
        net.add_output(pv, tname, Expression("%s.prod(%s, 1)" % (tname, tkX)))
        self.makeGuardAtomic(net, tname, tkX)
                    
    def tranTerm(self, net, v, r, gr, u):
        """ Translate a terminal, atomic action
        @param u: predecessor of v
        """
        tname = self.pushAtomic(net, r, v, gr, StrlType.TERM)
        p0 = self.placeName(r.name())
        pu = self.placeName(r.name(), u)
        tkI = self.tokenI(r.node(v).action())
        tkX = self.tokenX(r.node(v).action())
        net.add_input(p0, tname, Variable("%s" % tkX))
        net.add_input(pu, tname, Variable("%s" % tkI))
        net.add_output(p0, tname, Expression("%s.adds(%s, %s)" % (tname, tkX, tkI)))
        self.makeGuardAtomic(net, tname, tkI)
    
    def tranNorm(self, net, v, r, gr, u):
        """ Translate a normal atomic action
        @see: tranTerm(...)
        """
        tname = self.pushAtomic(net, r, v, gr, StrlType.NORM)
        pu = self.placeName(r.name(), u)
        pv = self.placeName(r.name(), v)
        net.add_place(Place(pv))
        tkI = self.tokenI(r.node(v).action())
        net.add_input(pu, tname, Variable("%s" % tkI))
        net.add_output(pv, tname, Variable("%s" % tkI))
        self.makeGuardAtomic(net, tname, tkI)
    
    def tranMono_Mono(self, net, tname1, tname2):
        """ Translate a sync between two mono actions
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
            
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.cip(net, tname1)
            net.add_input(p0, sname, Variable("X"))
            net.add_output(p0, sname, Variable("X"))
            self.makeGuardSync(net, sname, "X")
    
    def tranMono_Spwn(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
            
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.placeName(t1.rname())
            net.add_input(p0, sname, Variable("X"))
            pv2 = self.cop(net, tname2)
            if isinstance(t1.action(), Input):
                net.add_output(pv2, sname, Expression("%s.prod(X, 1)" % sname))
                net.add_output(p0, sname, Expression("%s.cons(X, 1)" % sname))
            else:
                net.add_output(pv2, sname, Expression("%s.prod(X, 2)" % sname))
                net.add_output(p0, sname, Expression("%s.cons(X, 2)" % sname))
            self.makeGuardSync(net, sname, "X")
            
    def tranMono_Term(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
            
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.cip(net, tname1)
            net.add_input(p0, sname, Variable("X"))
            
            pu2 = self.cip(net, tname2)
            tkI = self.tokenI(t2.action())
            net.add_input(pu2, sname, Variable("%s" % tkI))
            net.add_output(p0, sname, Expression("%s.adds(X, %s)" % (sname, tkI)))
            if isinstance(t1.action(), Input):
                self.makeGuardSync(net, sname, tkI, "X")
            else:
                self.makeGuardSync(net, sname, "X", tkI)
            
    def tranMono_Norm(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        self.fuse(net, tname1, tname2, sname)
        
    def tranSpwn_Spwn(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
            
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.cip(net, tname2)
            net.add_input(p0, sname, Variable("X"))
            net.add_output(p0, sname, Expression("%s.cons(X)" % sname))
            
            p1 = self.cop(net, tname1)
            p2 = self.cop(net, tname2)
            if isinstance(t1.action(), Input):
                net.add_output(p2, sname, Expression("%s.prod(X, 1)" % sname))
                net.add_output(p1, sname, Expression("%s.prod(X, 2)" % sname))
            else:
                net.add_output(p1, sname, Expression("%s.prod(X, 1)" % sname))
                net.add_output(p2, sname, Expression("%s.prod(X, 2)" % sname))
            self.makeGuardSync(net, sname, "X")
            
    def tranSpwn_Term(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
            
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.cip(net, tname1)
            pv1 = self.cop(net, tname1)
            pu2 = self.cip(net, tname2)
            tkI = self.tokenI(t2.action())
            net.add_input(p0, sname, Variable("X"))
            net.add_input(pu2, sname, Variable("%s" % tkI))
            net.add_output(pv1, sname, Expression("%s.prod(X, 1)" % sname))
            net.add_output(p0, sname, Expression("%s.adds(%s.cons(X, 1), %s)" % (sname, sname, tkI)))
            if isinstance(t1.action(), Output):
                self.makeGuardSync(net, sname, "X", tkI)
            else:
                self.makeGuardSync(net, sname, tkI, "X")
            
    def tranSpwn_Norm(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        self.fuse(net, tname1, tname2, sname)
        
    def tranTerm_Term(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
        if t1.rname() != t2.rname():
            self.fuse(net, tname1, tname2, sname)
        else:
            p0 = self.placeName(t1.rname())
            net.add_input(p0, sname, Variable("X"))
            pu1 = self.cip(net, tname1)
            pu2 = self.cip(net, tname2)
            tkI1 = self.tokenI(t1.action())
            tkI2 = self.tokenI(t2.action())
            if pu1 == pu2:
                net.add_input(pu1, sname, MultiArc([Variable("%s" % tkI1), Variable("%s" % tkI2)]))
            else:
                net.add_input(pu1, sname, Variable("%s" % tkI1))
                net.add_input(pu2, sname, Variable("%s" % tkI2))
            net.add_output(p0, sname, Expression("%s.adds(X, %s, %s)" % (sname, tkI1, tkI2)))
            if isinstance(t1.action(), Input):
                self.makeGuardSync(net, sname, tkI2, tkI1)
            else:
                self.makeGuardSync(net, sname, tkI1, tkI2)
            
    def tranTerm_Norm(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        self.fuse(net, tname1, tname2, sname)
        
    def tranNorm_Norm(self, net, tname1, tname2):
        """
        """
        sname = self.pushSync(net, tname1, tname2)
        self.fuse(net, tname1, tname2, sname)
        
    def pushAtomic(self, net, r, v, gr, stype):
        """ Push an atomic action into the net global names
        @param net: the net
        @param r: replicator
        @param r,v: position of the action
        @param stype: structural type of the action
        """
        tname = self.transName(r.name(), v)
        net.globals[tname] = NetAtomicTransition(r.node(v), r.name(), gr, stype)
        net.add_transition(Transition(tname))
        return tname
    
    def pushSync(self, net, tname1, tname2):
        """ Push a sync into the globals of net
        @param net: the net
        @param tname1: transition name corresponding to 
            the first observable atomic action
        @param tname2: transition name corresponding to
            the second observable atomic action
        """
        t1 = net.globals[tname1]
        t2 = net.globals[tname2]
        if isinstance(t1.action(), Output):
            sname = self.transName(tname1, tname2)
            net.globals[sname] = NetSyncTransition(t1, t2)
        else:
            sname = self.transName(tname2, tname1)
            net.globals[sname] = NetSyncTransition(t2, t1)
        net.add_transition(Transition(sname))
        return sname
    
    def cip(self, net, tname):
        """ Get a unique control input place
        @note: p0 may be the input places of a terminal action, it does not
            provide threads for performing the action but only for the technique.
            It must be omitted.
        """
        pG = self.placeName()
        names = net.pre(tname)
        names.discard(pG)
        if len(names) == 2:
            rname = net.globals[tname].rname()
            names.discard(self.placeName(rname))
        return names.pop()

    def cop(self, net, tname):
        """ Get A unique control output place
        @note: p0 may be in the output places of a spawning action, it does not
            produce thread after performing the action, but only for receiving
            the rest. It must be omitted.
        """
        pG = self.placeName()
        names = net.post(tname)
        names.discard(pG)
        if len(names) == 2:
            rname = net.globals[tname].rname()
            names.discard(self.placeName(rname))
        return names.pop()
    
    def fuse(self, net, tname1, tname2, sname):
        """ Merge two atomic transitions without renaming arc labels
        @param net: the net
        @param tname1: the first source transition name
        @param tname2: the second source transition name
        @param sname: name of the target transition (a sync transition)
        """
        pG = self.placeName()  
        t1 = net.transition(tname1)
        t2 = net.transition(tname2)
        # for control input places
        pre = {}
        for trans in [t1, t2]:
            places = trans.pre.keys()
            places.remove(pG)
            for place in places:
                if place not in pre:
                    pre[place] = []
                label = trans.pre[place]
                pre[place].append(label.copy())
        for place, labels in pre.items():
            if len(labels) == 1:
                net.add_input(place, sname, labels[0])
            else:
                net.add_input(place, sname, MultiArc(labels))
        # for control output places
        post = {}
        for trans in [t1, t2]:
            places = trans.post.keys()
            places.remove(pG)
            for place in places:
                if place not in post:
                    post[place] = []
                label = trans.post[place]
                post[place].append(label.copy())
        for place, labels in post.items():
            if len(labels) == 1:
                net.add_output(place, sname, labels[0])
            else:
                net.add_output(place, sname, MultiArc(labels))
        # make the guard
        pu1 = self.cip(net, tname1)
        pu2 = self.cip(net, tname2)
        tk1 = t1.pre[pu1]
        tk2 = t2.pre[pu2]
        a1 = net.globals[tname1].action()
        if isinstance(a1, Output):
            self.makeGuardSync(net, sname, tk1, tk2)
        else:
            self.makeGuardSync(net, sname, tk2, tk1)
    
    def makeGuardAtomic(self, net, tname, tk):
        """ Make the guard for an atomic transition
        @param net: the net
        @param tname: atomic transition name
        @param tk: input control token
        @note: G is the input context token
        """
        pG = self.placeName()
        net.add_input(pG, tname, Variable("G"))
        net.add_output(pG, tname, Expression("%s.post(G, %s)" % (tname, tk)))
        trans = net.transition(tname)
        trans.guard = Expression("%s.pre(G, %s)" % (tname, tk))
    
    def makeGuardSync(self, net, sname, tkOut, tkIn=None):
        """ Make the guard for a sync transition
        @param net: the net
        @param sname: sync transition name
        @param tkOut: control token for the output, or used for both
            output and input if tkIn is omitted
        @param tkIn: control token for the input
        @note: G is the input context token
        """
        pG = self.placeName()
        if pG in net.pre(sname):
            net.remove_input(pG, sname)
            net.remove_output(pG, sname)
        net.add_input(pG, sname, Variable("G"))
        
        if tkIn is None:
            guard = Expression("%s.pre(G, %s)" % (sname, tkOut))
            tkGo = Expression("%s.post(G,%s)" % (sname, tkOut))
        else:
            guard = Expression("%s.pre(G, %s, %s)" % (sname, tkOut, tkIn))
            tkGo = Expression("%s.post(G, %s, %s)" % (sname, tkOut, tkIn))
        trans = net.transition(sname)
        trans.guard = guard
        net.add_output(pG, sname, tkGo)
        
    
class NetTranslator(TranslationRules):
    """ Translating a pi-graph into a Petri-net
    """
    def __init__(self):
        self._bld = PiGraphBuilder()
    
    def inputSpec(self, spec):
        """ Input a pi-graph specification
        @param spec: specification of the pi-graph
        """
        self._bld.inputSpec(spec)
        self._grp = self._bld.build()
        self._net = PetriNet("PiPN")
    
    def createInitialPlace(self, net, r):
        """ Create a flow context place for a replicator
        @param net: the net
        @param r: the replicator
        """
        rname = r.name()
        bound = r.bound()
        pname = self.placeName(rname)
        net.add_place(Place(pname, [Generator(bound)]))
    
    def createContextPlace(self, net, gr):
        """ Create a name context place
        @param gr: the pi-graph
        """
        bld = ContextBuilder()
        nc = bld.buildNameContext(gr)
        pG = self.placeName()
        net.add_place(Place(pG, [nc]))
        
    def tranAtomics(self, net, gr):
        """ Translate atomic actions to atomic transitions
        @param gr: the pi-graph
        """
        for r in gr.replicator():
            self.createInitialPlace(net, r)
            inodes = r.initialNodes()
            fnodes = r.finalNodes()
            prev = {}
            queue = list(inodes)
            while len(queue) > 0:
                v = queue.pop(0)
                
                if v in inodes and v in fnodes:  # mono
                    self.tranMono(net, v, r, gr)
                elif v in inodes and v not in fnodes:  # spawning
                    self.tranSpwn(net, v, r, gr)
                elif v in fnodes and v not in inodes:  # term
                    self.tranTerm(net, v, r, gr, prev[v])
                else:  # norm
                    self.tranNorm(net, v, r, gr, prev[v])
                if v not in fnodes:
                    children = r.succ(v)
                    for w in children:
                        prev[w] = v
                        queue.append(w)
                
    def tranSyncs(self, net):
        """ Translate synchronizations to sync transitions
            based on the translated atomic transitions
        """
        tnameIs = []
        tnameOs = []
        for t in net.transition():
            tname = t.name
            a = net.globals[tname].action()
            if isinstance(a, Input):
                tnameIs.append(tname)
            elif isinstance(a, Output):
                tnameOs.append(tname)
        for tnameO in tnameOs:
            for tnameI in tnameIs:
                to = net.globals[tnameO]
                ti = net.globals[tnameI]
                if to.isMono() and ti.isMono():
                    self.tranMono_Mono(net, tnameO, tnameI)
                elif to.isMono() and ti.isSpwn():
                    self.tranMono_Spwn(net, tnameO, tnameI)
                elif to.isMono() and ti.isTerm():
                    self.tranMono_Term(net, tnameO, tnameI)
                elif to.isMono() and ti.isNorm():
                    self.tranMono_Norm(net, tnameO, tnameI)
                elif to.isSpwn() and ti.isMono():
                    self.tranMono_Spwn(net, tnameI, tnameO)
                elif to.isSpwn() and ti.isSpwn():
                    self.tranSpwn_Spwn(net, tnameO, tnameI)
                elif to.isSpwn() and ti.isTerm():
                    self.tranSpwn_Term(net, tnameO, tnameI)
                elif to.isSpwn() and ti.isNorm():
                    self.tranSpwn_Norm(net, tnameO, tnameI)
                elif to.isTerm() and ti.isMono():
                    self.tranMono_Term(net, tnameI, tnameO)
                elif to.isTerm() and ti.isSpwn():
                    self.tranSpwn_Term(net, tnameI, tnameO)
                elif to.isTerm() and ti.isTerm():
                    self.tranTerm_Term(net, tnameO, tnameI)
                elif to.isTerm() and ti.isNorm():
                    self.tranTerm_Norm(net, tnameO, tnameI)
                elif to.isNorm() and ti.isMono():
                    self.tranMono_Norm(net, tnameI, tnameO)
                elif to.isNorm() and ti.isSpwn():
                    self.tranSpwn_Norm(net, tnameI, tnameO)
                elif to.isNorm() and ti.isTerm():
                    self.tranTerm_Norm(net, tnameI, tnameO)
                else:
                    self.tranNorm_Norm(net, tnameO, tnameI)
                
    def translate(self):
        """ Do the translation
        """
        self.createContextPlace(self._net, self._grp)
        self.tranAtomics(self._net, self._grp)
        self.tranSyncs(self._net)
        return self._net
